﻿/*
 * *******************************************************
 *
 * 作者：hzy
 *
 * 开源地址：https://gitee.com/hzy6
 *
 * *******************************************************
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using HzyScanDiService.Attributes;
using Microsoft.Extensions.DependencyInjection;

namespace HzyScanDiService.Extensions
{

    /// <summary>
    /// 服务扫描扩展
    /// </summary>
    public static class DiServiceScanningExtensions
    {
        /// <summary>
        /// 扫描服务 自动注入服务
        /// </summary>
        /// <param name="serviceCollection"></param>
        /// <param name="assemblyFilter"></param>
        public static void AddHzyScanDiService(this IServiceCollection serviceCollection, string assemblyFilter = "")
        {
            IEnumerable<Assembly> assemblies = default;

            if (string.IsNullOrWhiteSpace(assemblyFilter))
            {
                assemblies = DiServiceScanningExtensions.GetAssemblyList();
            }
            else
            {
                assemblies = DiServiceScanningExtensions.GetAssemblyList(w =>
                {
                    var name = w.GetName().Name;
                    return name != null && name.StartsWith(assemblyFilter);
                });
            }

            if (assemblies == null) return;

            var _diServiceScanningProvider = new DiServiceScanningProvider();

            #region DI Interface 接口注册

            _diServiceScanningProvider.ScanningDiInterface(serviceCollection, assemblies);

            #endregion

            #region AopBase 特性 方式 自动注册

            var isAopBaseAttribute = assemblies.Where(w => !w.IsDynamic).Any(w =>

                  w.ExportedTypes
                  .Any(a => a.GetCustomAttribute(typeof(AopBaseAttribute)) != null || a.BaseType?.GetCustomAttribute(typeof(AopBaseAttribute)) != null)

              );

            if (isAopBaseAttribute)
            {
                //仅仅注册组件
                NatashaInitializer.Initialize();
                //注册+预热组件 , 之后编译会更加快速
                NatashaInitializer.InitializeAndPreheating().Wait();

                foreach (var item in assemblies.Where(w => !w.IsDynamic))
                {
                    var classList = item.ExportedTypes
                           .Where(w => w.GetCustomAttribute(typeof(AopBaseAttribute)) != null || w.BaseType?.GetCustomAttribute(typeof(AopBaseAttribute)) != null);

                    foreach (var _class in classList)
                    {
                        _diServiceScanningProvider.ScanningAopBaseAttribute(serviceCollection, _class);
                    }

                }
            }

            #endregion
        }

        /// <summary>
        /// 注册 服务提供者
        /// </summary>
        /// <param name="serviceProvider"></param>
        public static void UseServiceProvider(this IServiceProvider serviceProvider)
        {
            ServiceProviderExtensions.UseServiceProvider(serviceProvider);
        }

        /// <summary>
        /// 获取所有的 程序集
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public static IEnumerable<Assembly> GetAssemblyList(Func<Assembly, bool> where = null)
        {
            #region 查找手动引用的程序集
            IEnumerable<Assembly> assemblies = new List<Assembly>();
            var entryAssembly = Assembly.GetEntryAssembly();
            if (entryAssembly == null) return assemblies;
            var referencedAssemblies = entryAssembly.GetReferencedAssemblies().Select(Assembly.Load);
            assemblies = new List<Assembly> { entryAssembly }.Union(referencedAssemblies);
            #endregion

            #region 将所有 dll 文件 重新载入 防止有未扫描到的 程序集
            var paths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory)
                .Where(w => w.EndsWith(".dll") && !w.Contains(nameof(Microsoft)))
                .Select(w => w)
             ;
            foreach (var item in paths)
            {
                if (File.Exists(item))
                {
                    try
                    {
                        Assembly.Load(AssemblyLoadContext.GetAssemblyName(item));
                    }
                    catch (Exception)
                    {

                    }
                }
            }
            assemblies = AssemblyLoadContext.Default.Assemblies.Union(assemblies);
            #endregion

            return @where == null ? assemblies : assemblies.Where(@where);
        }

    }


}
